﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LuaInterface;
using System.Collections;
using System.Reflection;
using Microsoft.Xna.Framework;

namespace System.Lua.Serialization
{
    public partial class LuaSerializer
    {

        public object Deserialize(string fileName)
        {
            try
            {
                lua.DoFile(fileName);
            }
            catch (Exception ex)
            {
                Console.WriteLine("載入失敗0:" + ex.Message);
            }

            LuaTable table = lua.GetTable(TableName);
            object item = null;
            DeserializeImpl(table, ref item);
            return item;
        }

        private void DeserializeImpl(object value, ref object item)
        {
            if (value == null)
                item = value;
            else if (value.GetType().IsDouble())
                DeserializeDouble(value, ref item);
            else if (value.GetType().IsString())
                DeserializeString(value, ref item);
            else if (value.GetType().IsBool())
                DeserializeBool(value, ref item);
            else if (value.GetType().IsLuaTable())
                DeserializeLuaTable((LuaTable)value, ref item);
        }

        private void DeserializeLuaTable(LuaTable table, ref object item)
        {
            if (table.IsClassOrStruct())
                DeserializeClassOrStruct(table, ref item);
            else if (table.IsEnumerable())
                DeserializeEnumerable(table, ref item);
        }

        private void DeserializeClassOrStruct(LuaTable table, ref object item)
        {
            object _item;
            if (item == null)
                _item = CreateInstance(table);
            else _item = item;

            Type _type = _item.GetType();

            foreach (var propertyInfo in _type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                if (propertyInfo.CanRead && propertyInfo.CanWrite)
                {
                    object pValue = propertyInfo.GetValue(_item, null);
                    object tValue = table[propertyInfo.Name];
                    DeserializeImpl(tValue, ref pValue);
                    if (pValue!=null&&pValue.GetType().IsPrimitive)
                        pValue = Convert.ChangeType(pValue, propertyInfo.PropertyType);
                    propertyInfo.SetValue(_item, pValue, null);
                }
            }

            foreach (var fieldInfo in _type.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                object pValue = fieldInfo.GetValue(_item);
                object tValue = table[fieldInfo.Name];
                DeserializeImpl(tValue, ref pValue);
                if (pValue != null && pValue.GetType().IsPrimitive)
                    pValue = Convert.ChangeType(pValue, fieldInfo.FieldType);
                fieldInfo.SetValue(_item, pValue);
            }
            item = _item;
        }

        private void DeserializeEnumerable(LuaTable table, ref object item)
        {
            if (item == null)
                throw new Exception("item");
            if (item.GetType().SerializeAsDictionary())
            {
                IDictionary dict = (IDictionary)item;
                DeserializeDictionary(table, ref dict);
            }
            else if (item.GetType().SerializeAsEnumerable())
            {
                IList list = (IList)item;
                DeserializeList(table, ref list);
            }
        }

        private void DeserializeList(LuaTable table, ref IList item)
        {
            if (item == null)
                throw new Exception("item");
            foreach (var value in table.Values)
            {
                object listItem = null;
                DeserializeImpl(value, ref listItem);
                item.Add(listItem);
            }
        }

        private void DeserializeDictionary(LuaTable table, ref IDictionary item)
        {
            if (item == null)
                throw new Exception("item");
            foreach (var value in table.Values)
            {
                object listItem = null;
                DeserializeImpl(value, ref listItem);
                if (listItem != null && listItem.GetType() == typeof(DictionaryEntry))
                {
                    DictionaryEntry de = (DictionaryEntry)listItem;
                    item.Add(de.Key, de.Value);
                }
            }
        }

        private void DeserializeDouble(object value, ref object item)
        {
            item = Convert.ToDouble(value);
        }

        private void DeserializeBool(object value, ref object item)
        {
            item = Convert.ToBoolean(value);
        }

        private void DeserializeString(object value, ref object item)
        {
            item = value.ToString();
        }

        private object CreateInstance(LuaTable table)
        {
            string typeStr = (string)table[TypeTableAttribute];
            Type _type = Type.GetType(typeStr, true);
            object instance = Activator.CreateInstance(_type);
            return instance;
        }


    }
}
